#include "conductor.h"

void websocketSendMessage(const std::string& message);

namespace {
// Names used for a IceCandidate JSON object.
    const char kCandidateSdpMidName[] = "sdpMid";
    const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
    const char kCandidateSdpName[] = "candidate";

// Names used for a SessionDescription JSON object.
    const char kSessionDescriptionTypeName[] = "type";
    const char kSessionDescriptionSdpName[] = "sdp";

    class DummySetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
    public:
        static DummySetSessionDescriptionObserver* Create() {
            return new rtc::RefCountedObject<DummySetSessionDescriptionObserver>();
        }
        virtual void OnSuccess() { RTC_LOG(INFO) << __FUNCTION__; }
        virtual void OnFailure(webrtc::RTCError error) {
            RTC_LOG(INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " << error.message();
        }
    };
}  // namespace

Conductor::Conductor() : fake_capturer(nullptr), need_hangeup(false), iceCandidateList(Json::arrayValue) {
}

Conductor::~Conductor() {
  RTC_DCHECK(!peer_connection_);
}

bool Conductor::connection_active() const {
  return peer_connection_ != nullptr;
}

void Conductor::Close() {
  DeletePeerConnection();
}

bool Conductor::InitializePeerConnection() {
    RTC_DCHECK(!peer_connection_factory_);
    RTC_DCHECK(!peer_connection_);

    peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
        nullptr /* network_thread */, nullptr /* worker_thread */,
        nullptr /* signaling_thread */, nullptr /* default_adm */,
        webrtc::CreateBuiltinAudioEncoderFactory(),
        webrtc::CreateBuiltinAudioDecoderFactory(),
        webrtc::CreateBuiltinVideoEncoderFactory(),
        webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */,
        nullptr /* audio_processing */);

    RTC_LOG(INFO) << __FUNCTION__ << " here ";
    if (!peer_connection_factory_) {
        RTC_LOG(INFO) << __FUNCTION__ << " error: peer_connection_factory_ is null ";
        DeletePeerConnection();
        return false;
    }

    if (!CreatePeerConnection(/*dtls=*/true)) {
        DeletePeerConnection();
    }
    AddTracks();
    return peer_connection_ != nullptr;
}

bool Conductor::CreatePeerConnection(bool dtls) {
  RTC_DCHECK(peer_connection_factory_);
  RTC_DCHECK(!peer_connection_);

  webrtc::PeerConnectionInterface::RTCConfiguration config;
  config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
  config.enable_dtls_srtp = dtls;
  std::string coturnUserName = "webrtciimuserzxzq";
  std::string password = "zxzq852258";
  std::string host = "test.iimiim.cn";

  webrtc::PeerConnectionInterface::IceServer server1;
  server1.uri = "turn:" + host + ":3478?transport=udp";
  server1.username = coturnUserName;
  server1.password = password;
  config.servers.push_back(server1);

  webrtc::PeerConnectionInterface::IceServer server2;
  server2.uri = "turn:" + host + ":3478?transport=tcp";
  server2.username = coturnUserName;
  server2.password = password;
  config.servers.push_back(server2);

  webrtc::PeerConnectionInterface::IceServer server3;
  server3.uri = "turn:" + host + ":5349?transport=udp";
  server3.username = coturnUserName;
  server3.password = password;
  config.servers.push_back(server3);

  webrtc::PeerConnectionInterface::IceServer server4;
  server4.uri = "turn:" + host + ":5349?transport=tcp";
  server4.username = coturnUserName;
  server4.password = password;
  config.servers.push_back(server4);

  peer_connection_ = peer_connection_factory_->CreatePeerConnection(config, nullptr, nullptr, this);
  return peer_connection_ != nullptr;
}

void Conductor::DeletePeerConnection() {
  peer_connection_ = nullptr;
  peer_connection_factory_ = nullptr;
}

// PeerConnectionObserver implementation.
void Conductor::OnAddTrack(
    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
    const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>&
        streams) {
  RTC_LOG(INFO) << __FUNCTION__ << " " << receiver->id();
}

void Conductor::OnRemoveTrack(
    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) {
  RTC_LOG(INFO) << __FUNCTION__ << " " << receiver->id();
}

void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
    Json::Value jmessage;
    Json::Value message_mid(candidate->sdp_mid());
    Json::Value message_mline_index(candidate->sdp_mline_index());
    jmessage[kCandidateSdpMidName] = message_mid;
    jmessage[kCandidateSdpMlineIndexName] = message_mline_index;
    std::string sdp;
    if (!candidate->ToString(&sdp)) {
        RTC_LOG(INFO) << "Failed to serialize candidate";
        return;
    }
    jmessage[kCandidateSdpName] = sdp;
    iceCandidateList.append(jmessage);
}

void Conductor::AddTracks() {
    if (!peer_connection_->GetSenders().empty()) {
        return;  // Already added tracks.
    }

    rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track_;
    fake_capturer = new VcmCapturer();
    videoSource = TrackSource::Create(fake_capturer);

    if (!videoSource) {
        RTC_LOG(LS_ERROR) << "Cannot create capturer video";
    } else {
        const char kVideoLabel[] = "video_label";
        video_track_ = peer_connection_factory_->CreateVideoTrack(kVideoLabel, videoSource);
        const char kStreamId[] = "video_stream_id";
        auto result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId});
        if (!result_or_error.ok()) {
            RTC_LOG(LS_ERROR) << "Failed to add video track to PeerConnection: " << result_or_error.error().message();
        }
    }
}

// CreateSessionDescriptionObserver implementation.
void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
  peer_connection_->SetLocalDescription(DummySetSessionDescriptionObserver::Create(), desc);

  std::string sdp;
  desc->ToString(&sdp);
  Json::Value jmessage;
  jmessage[kSessionDescriptionTypeName] = webrtc::SdpTypeToString(desc->GetType());
  jmessage[kSessionDescriptionSdpName] = sdp;

  Json::Value offer_json;
  offer_json["payload"] = jmessage;
  Json::Value peer_name(peerid);
  offer_json["name"] = peer_name;
  Json::Value type_local("offer");
  offer_json["type"] = type_local;
  std::string payload_str = rtc::JsonValueToString(offer_json);
  websocketSendMessage(payload_str);
}

void Conductor::OnFailure(webrtc::RTCError error) {
  RTC_LOG(LERROR) << ToString(error.type()) << ": " << error.message();
}

void Conductor::createOffer(std::string &peer_id_) {
    if(InitializePeerConnection()) {
        peerid = peer_id_;
        peer_connection_->CreateOffer(this, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
    } else {
        RTC_LOG(LS_ERROR) << "Failed to initialize PeerConnection";
    }
}

void Conductor::setAnswer(const Json::Value& jmessage) {
    std::string type;
    std::string sdp;
    if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type)
        || !rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName, &sdp)) {
        RTC_LOG(WARNING) << "Can't parse received message.";
    } else {
        webrtc::SdpParseError error;
        std::unique_ptr<webrtc::SessionDescriptionInterface> session_description(webrtc::CreateSessionDescription(type, sdp, &error));
        if (!session_description) {
            RTC_LOG(WARNING) << "Can't parse received session description message. "
                             << "SdpParseError was: " << error.description;
            return;
        }
        RTC_LOG(INFO) << " Received session description :" << jmessage;
        peer_connection_->SetRemoteDescription(DummySetSessionDescriptionObserver::Create(), session_description.release());
    }
}

void Conductor::addIceCandidate(const Json::Value& jmessage) {
    std::string sdp_mid;
    int sdp_mlineindex = 0;
    std::string sdp;
    if (  !rtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName, &sdp_mid)
          || !rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName, &sdp_mlineindex)
          || !rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
        RTC_LOG(WARNING) << "Can't parse received message:" << jmessage;
    } else {

        webrtc::SdpParseError error;
        std::unique_ptr<webrtc::IceCandidateInterface> candidate(
            webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error));
        if (!candidate.get()) {
            RTC_LOG(WARNING) << "Can't parse received candidate message. " << "SdpParseError was: " << error.description;
            return;
        }
        if (!peer_connection_->AddIceCandidate(candidate.get())) {
            RTC_LOG(WARNING) << "Failed to apply the received candidate";
            return;
        }
        RTC_LOG(INFO) << " Received candidate :" << jmessage;
    }
}

void Conductor::hangUp() {
    peerid = "";
    iceCandidateList.clear();
    sendCadateString = "";
    if(videoSource != nullptr) videoSource = nullptr;
    if(peer_connection_ != nullptr) peer_connection_ = nullptr;
    if(peer_connection_factory_ != nullptr) peer_connection_factory_ = nullptr;
    if(fake_capturer != nullptr){
        delete fake_capturer;
        fake_capturer = nullptr;
    }
}
